﻿namespace Atomic.Plugins
{
    using System;
    using System.IO;

    using Atomic.Extensions;
    using Atomic.Plugins.Files;

    /// <summary>
    /// 默认插件加载器。
    /// </summary>
    public class DefaultPluginLoader : IPluginLoader
    {
        /// <summary>
        /// 插件描述信息。
        /// </summary>
        private readonly IPluginInitializer _pluginInitializer = null;

        /// <summary>
        /// 虚拟路径提供程序。
        /// </summary>
        private readonly IVirtualPathProvider _virtualPathProvider = null;

        /// <summary>
        /// 程序集探测目录。
        /// </summary>
        private readonly IAssemblyProbingFolder _assemblyProbingFolder = null;

        /// <summary>
        /// 初始化默认插件加载器。
        /// </summary>
        /// <param name="pluginInitializer">插件描述信息。</param>
        /// <param name="virtualPathProvider">虚拟路径提供程序。</param>
        /// <param name="assemblyProbingFolder">程序集探测目录。</param>
        public DefaultPluginLoader(IPluginInitializer pluginInitializer, IVirtualPathProvider virtualPathProvider, IAssemblyProbingFolder assemblyProbingFolder)
        {
            this._pluginInitializer = pluginInitializer;
            this._virtualPathProvider = virtualPathProvider;
            this._assemblyProbingFolder = assemblyProbingFolder;
        }

        /// <summary>
        /// 激活。
        /// </summary>
        /// <param name="pluginDescriptor">插件描述信息。</param>
        public void Activate(PluginDescriptor pluginDescriptor)
        {
            //转移程序集到应用目录。
            this.DependencyActivated(pluginDescriptor);

            //初始化插件。
            this._pluginInitializer.Initialize(pluginDescriptor);
        }

        /// <summary>
        /// 激活关联项。
        /// </summary>
        /// <param name="plugin">关联项描述信息。</param>
        /// <returns></returns>
        private void DependencyActivated(PluginDescriptor plugin)
        {
            if (StringComparer.OrdinalIgnoreCase.Equals(Path.GetExtension(plugin.Dependency.VirtualPath), ".dll"))
            {
                //激活关联项。
                this.ReferenceActivated(plugin.Dependency.Name, plugin.Dependency.VirtualPath);

                //激活关联引用项。
                plugin.Dependency.References.ForEach(reference => this.ReferenceActivated(reference.Name, reference.VirtualPath));
            }
        }

        /// <summary>
        /// 激活关联引用项。
        /// </summary>
        /// <remarks>
        /// 将插件目录里的程序集复制到 Probing 目录下。
        /// </remarks>
        /// <param name="referenceName">关联文件名。</param>
        /// <param name="virtualPath">关联文件的虚拟路径。</param>
        private void ReferenceActivated(string referenceName, string virtualPath)
        {
            string sourceFileName = this._virtualPathProvider.MapPath(virtualPath);

            //程序集不存在或者源程序集的修改时间大于 Probing 目录的修改时间时，可以进行程序集复制操作。
            bool copyAssembly =
                !this._assemblyProbingFolder.AssemblyExists(referenceName) ||
                File.GetLastWriteTimeUtc(sourceFileName) > this._assemblyProbingFolder.GetAssemblyDateTimeUtc(referenceName);

            if (copyAssembly)
            {
                this._assemblyProbingFolder.StoreAssembly(referenceName, sourceFileName);
            }
        }
    }
}